import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;

/**
 * JSClient - Jam Session Client
 * 
 * @author James Varvaro
 */
public class JSClient extends JFrame implements ActionListener {
    private Socket socket;
    private Drop drop;
    private Drop userInputDrop;
    
    private MidiController soundPlayer;
    private JSMsgProtocolClient protocol;
    
    private volatile boolean connected = false;
    private boolean running = true;
    
    private static JSClient client;
    private PrintWriter out;
    private BufferedReader in;
    
    private User user;
    private static String reqUsername;
    
    private InstrumentPanel instrumentPanel;
    
    private JMenuBar mainMenuBar;
    private JMenu connectMenu;
    private JMenuItem connectMenuItem;
    private JMenuItem disconnectMenuItem;
    private JMenuItem exitMenuItem;
    private JMenuItem lobbyMenuItem;
    private ClientLobbyDialog lobbyDialog;
    
    public JSClient(final InetAddress hostAddress, final int hostPort) throws Exception {
        // Unconnected socket
        this.socket = new Socket();
        drop = new Drop();
        userInputDrop = new Drop();
        this.protocol = new JSMsgProtocolClient(this);
        this.soundPlayer = new MidiController();
        
        this.running = true;
        this.connected = false;
        
        
        this.user = new User(-1, "test", "piano");
        
        
        
        // init GUI
        this.setSize(640, 480);
        this.setTitle("Jam Session Client");
        /*
        this.setLayout(new GridLayout(1,12));
        // init Piano Keys
        String[] keylabels = {"Glow", "A", "Bb", "B", "C", "C#", "D", "Eb", "E", "F", "F#", "G"};
        for(int i=0; i < 12; i++) {
            JButton key = new JButton(keylabels[i]);
            key.setActionCommand(Integer.toString(i));
            //key.addActionListener(this);
            key.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    JButton source = (JButton)e.getSource();
                    ButtonModel sourceModel = source.getModel();
                    // Button press
                    if(sourceModel.isPressed()){
                        String command = source.getActionCommand();
                        String msg = "NOTE " + user.getUserid() + " " + user.getUsername() + " 1 piano " + command + " true";
                        out.println(msg);
                    }
                    // Button release
                    else if(!sourceModel.isPressed()) {
                        String command = source.getActionCommand();
                        String msg = "NOTE " + user.getUserid() + " " + user.getUsername() + " 1 piano " + command + " false";
                        out.println(msg);
                    }
                }
            });
            add(key);
        }
        */
        instrumentPanel = new InstrumentPanel(this);
        this.add(instrumentPanel, BorderLayout.PAGE_START);
        this.pack();
        this.setVisible(true);
        
        // lobby dialog setup
        lobbyDialog = new ClientLobbyDialog(this, this);
        
        // Setup menu
        mainMenuBar = new JMenuBar();
        connectMenu = new JMenu("Connection");
        connectMenuItem = new JMenuItem("Connect");
        disconnectMenuItem = new JMenuItem("Disconnect");
        exitMenuItem = new JMenuItem("Exit");
        lobbyMenuItem = new JMenuItem("Lobby");
        
        connectMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    jsConnect(hostAddress, hostPort);
                    disconnectMenuItem.setEnabled(true);
                    connectMenuItem.setEnabled(false);
                }
                catch(IOException ex) {
                    System.out.println("Could not connect to server.");
                    System.exit(-1);
                }
            }
        } );
        
        
        disconnectMenuItem.setEnabled(false);
        disconnectMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                disconnectUser();
                connectMenuItem.setEnabled(true);
                disconnectMenuItem.setEnabled(false);
            }
        } );

        lobbyMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                lobbyDialog.setVisible(true);
            }
        } );
        
        exitMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                //this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
            }
        } );
        
        connectMenu.add(connectMenuItem);
        connectMenu.add(exitMenuItem);
        connectMenu.add(disconnectMenuItem);
        connectMenu.add(lobbyMenuItem);
        mainMenuBar.add(connectMenu);
        this.setJMenuBar(mainMenuBar);
        
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        //new UserInputHandler(userInputDrop).start();
    }
    
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        String msg = "NOTE " + user.getUserid() + " " + user.getUsername() + " 1 piano " + command + " true";
        out.println(msg);
    }
    
    public void startNote(int noteid, String instrumentType, int pitch, String noteStatus) throws Exception{
        soundPlayer.playNote(noteid, instrumentType, pitch);
    }
    
    public void endNote(int noteid, String instrumentType, int pitch, String noteStatus) throws Exception{
        soundPlayer.stopNote(noteid, instrumentType, pitch);
    }
    
    public boolean isUserInit() {
        return user.isInit();
    }
    
    public String getUsername() {
        return user.getUsername();
    }
    
    public void initUser(int userid, String username, String instrument) {
        this.user = new User(userid, username, instrument);
        user.setInit();
        System.out.println(username + " connected to server.");
    }
    
    public User getUser() {
        return user;
    }
    
    public void connectUser(String username) {
        String msg = "CONNECTION -1 " + username + " connect " + "true";
        out.println(msg);
    }
    
    public void disconnectUser() {
        String msg = "CONNECTION " + user.getUserid() + " " + user.getUsername() + " connect " + "false";
        out.println(msg);
        try {
            this.socket.close();
        }
        catch(IOException ex) {
            System.out.println("Could not close client socket.");
        }
    }
    
    public void sendMsg(String msg) {
        out.println(msg);
    }
    // Initiates connection to server
    public void jsConnect(InetAddress hostAddress, int hostPort) throws IOException {
        // Do not reuse a closed Socket!
        if(this.socket.isClosed()){
            this.socket = new Socket();
        }
        this.socket.connect(new InetSocketAddress(hostAddress, hostPort));
        
        
        
        this.client.out = new PrintWriter(client.socket.getOutputStream(), true);
        this.client.in = new BufferedReader(
                                new InputStreamReader(
                                    client.socket.getInputStream()));

        this.client.connectUser(this.reqUsername);
        new InputHandler(socket, drop).start();
        
        client.connected = true;
    }
    
    public void createSession(String name) {
        
    }
    
    public static void main(String[] args) throws Exception {
        // args should be hostAddress, hostPort
        InetAddress argHost = InetAddress.getByName("localhost");
        int argPort = 5555;
        reqUsername = "Jimmy";
        if(args.length == 0) {
            
        }
        else if(args.length == 2) {
            try {
                argHost = InetAddress.getByName(args[0]);
                argPort = Integer.parseInt(args[1]);
            }
            catch (NumberFormatException ex) {
                System.err.println("Port argument must be an integer");
                System.exit(-1);
            }
        }
        else if(args.length == 3) {
            try {
                argHost = InetAddress.getByName(args[0]);
                argPort = Integer.parseInt(args[1]);
                reqUsername = args[2];
            }
            catch (NumberFormatException ex) {
                System.err.println("Port argument must be an integer");
                System.exit(-1);
            }
        }
        else {
            System.err.println("JSServer takes 0, 2(or 3) arguments: hostaddress, port, [username]");
            System.exit(-1);
        }
        
        // init client
        client = new JSClient(argHost, argPort);
        
        String testStr = "This server is on fire.";
        System.out.println("client init");
        while(client.running) {
            if(client.connected) {
                String data = null;
                // Get all messages in drop
                while((data = client.drop.poll()) != null) {
                    System.out.println(data);
                    client.protocol.process(data);
                }
            }
        }
    }
}

